home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / hplip / ui4 / devmgr5.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  73.8 KB  |  2,125 lines

  1. # -*- coding: utf-8 -*-
  2. #
  3. # (c) Copyright 2001-2009 Hewlett-Packard Development Company, L.P.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  18. #
  19. # Authors: Don Welch
  20. #
  21.  
  22. #from __future__ import generators
  23.  
  24. # Std Lib
  25. import sys
  26. import time
  27. import os
  28. import gzip
  29. import select
  30. import struct
  31.  
  32. # Local
  33. from base.g import *
  34. from base import device, utils, pml, maint, models
  35. from prnt import cups
  36. from base.codes import *
  37. from ui_utils import *
  38.  
  39. # Qt
  40. from PyQt4.QtCore import *
  41. from PyQt4.QtGui import *
  42.  
  43. # dbus
  44. import dbus
  45. from dbus.mainloop.qt import DBusQtMainLoop
  46. from dbus import lowlevel
  47.  
  48. # Main form
  49. from devmgr5_base import Ui_MainWindow
  50.  
  51. # Aux. dialogs
  52. from faxsetupdialog import FaxSetupDialog
  53. from plugindialog import PluginDialog
  54. from firmwaredialog import FirmwareDialog
  55. from aligndialog import AlignDialog
  56. from printdialog import PrintDialog
  57. from makecopiesdialog import MakeCopiesDialog
  58. from sendfaxdialog import SendFaxDialog
  59. from fabwindow import FABWindow
  60. from devicesetupdialog import DeviceSetupDialog
  61. from printtestpagedialog import PrintTestPageDialog
  62. from infodialog import InfoDialog
  63. from cleandialog import CleanDialog
  64. from colorcaldialog import ColorCalDialog
  65. from linefeedcaldialog import LineFeedCalDialog
  66. from pqdiagdialog import PQDiagDialog
  67. from nodevicesdialog import NoDevicesDialog
  68. from aboutdialog import AboutDialog
  69.  
  70. # Other forms and controls
  71. from settingsdialog import SettingsDialog
  72. from printsettingstoolbox import PrintSettingsToolbox
  73.  
  74.  
  75. # all in seconds
  76. MIN_AUTO_REFRESH_RATE = 5
  77. MAX_AUTO_REFRESH_RATE = 60
  78. DEF_AUTO_REFRESH_RATE = 30
  79.  
  80.  
  81. device_list = {}    # { Device_URI : device.Device(, ... }
  82. model_obj = models.ModelData() # Used to convert dbus xformed data back to plain Python types
  83.  
  84.  
  85. # ***********************************************************************************
  86. #
  87. # ITEM/UTILITY UI CLASSES
  88. #
  89. # ***********************************************************************************
  90.  
  91.  
  92. class FuncViewItem(QListWidgetItem):
  93.     def __init__(self, parent, text, pixmap, tooltip_text, cmd):
  94.         QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
  95.         self.tooltip_text = tooltip_text
  96.         self.cmd = cmd
  97.  
  98.  
  99.  
  100. class DeviceViewItem(QListWidgetItem):
  101.     def __init__(self, parent, text, pixmap, device_uri, is_avail=True):
  102.         QListWidgetItem.__init__(self, QIcon(pixmap), text, parent)
  103.         self.device_uri = device_uri
  104.         self.is_avail = is_avail
  105.         self.setTextAlignment(Qt.AlignHCenter)
  106.  
  107.  
  108.  
  109. class PluginInstall(QObject):
  110.     def __init__(self, parent, plugin_type, plugin_installed):
  111.         self.parent = parent
  112.         self.plugin_type = plugin_type
  113.         self.plugin_installed = plugin_installed
  114.  
  115.  
  116.     def exec_(self):
  117.         install_plugin = True
  118.  
  119.         if self.plugin_installed:
  120.             i = QMessageBox.warning(self.parent,
  121.                 self.parent.windowTitle(),
  122.                 self.__tr("<b>The HPLIP plugin is already installed.</b><p>Do you want to continue and re-install it?"),
  123.                 QMessageBox.Yes,
  124.                 QMessageBox.No,
  125.                 QMessageBox.NoButton)
  126.  
  127.             install_plugin = (i == QMessageBox.Yes)
  128.  
  129.         if install_plugin:
  130.             su_sudo_str = su_sudo()
  131.             if su_sudo_str is None:
  132.                 QMessageBox.critical(self.parent,
  133.                     self.parent.windowTitle(),
  134.                     self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-plugin.</b>"),
  135.                     QMessageBox.Ok,
  136.                     QMessageBox.NoButton,
  137.                     QMessageBox.NoButton)
  138.  
  139.             else:
  140.                 if utils.which('hp-plugin'):
  141.                     cmd = su_sudo_str % 'hp-plugin'
  142.                 else:
  143.                     cmd = su_sudo_str % 'python ./plugin.py'
  144.  
  145.                 log.debug(cmd)
  146.                 utils.run(cmd, log_output=True, password_func=None, timeout=1)
  147.  
  148.  
  149.     def __tr(self,s,c = None):
  150.         return qApp.translate("DevMgr5",s,c)
  151.  
  152.  
  153.  
  154. # ***********************************************************************************
  155. #
  156. # MAINWINDOW
  157. #
  158. # ***********************************************************************************
  159.  
  160. class DevMgr5(QMainWindow,  Ui_MainWindow):
  161.     def __init__(self,  toolbox_version, initial_device_uri=None,
  162.                  dbus_loop=None, parent=None, name=None, fl=0):
  163.  
  164.         QMainWindow.__init__(self, parent)
  165.  
  166.         log.debug("Initializing toolbox UI (Qt4)...")
  167.         log.debug("HPLIP Version: %s" % prop.installed_version)
  168.  
  169.         self.setupUi(self)
  170.  
  171.         self.toolbox_version = toolbox_version
  172.         self.initial_device_uri = initial_device_uri
  173.         self.device_vars = {}
  174.         self.num_devices = 0
  175.         self.cur_device = None
  176.         self.cur_printer = None
  177.         self.updating = False
  178.         self.init_failed = False
  179.         self.service = None
  180.  
  181.         # User settings
  182.         self.user_settings = UserSettings()
  183.         self.user_settings.load()
  184.         self.user_settings.debug()
  185.         self.cur_device_uri = self.user_settings.last_used_device_uri
  186.  
  187.         # Other initialization
  188.         self.initDBus()
  189.         self.initPixmaps()
  190.         self.initMisc()
  191.         self.initUI()
  192.  
  193.         cups.setPasswordCallback(showPasswordUI)
  194.  
  195.         if not prop.doc_build:
  196.             self.ContentsAction.setEnabled(False)
  197.  
  198.         self.allow_auto_refresh = True
  199.         QTimer.singleShot(0, self.initalUpdate)
  200.  
  201.  
  202.     # ***********************************************************************************
  203.     #
  204.     # INIT
  205.     #
  206.     # ***********************************************************************************
  207.  
  208.     # TODO: Make sbus init mandatory success, else exit
  209.     def initDBus(self):
  210.         self.dbus_loop = DBusQtMainLoop(set_as_default=True)
  211.         self.dbus_avail, self.service, self.session_bus = device.init_dbus(self.dbus_loop)
  212.  
  213.         if not self.dbus_avail:
  214.             log.error("dBus initialization error. Exiting.")
  215.             self.init_failed = True
  216.             return
  217.  
  218.         # Receive events from the session bus
  219.         self.session_bus.add_signal_receiver(self.handleSessionSignal, sender_keyword='sender',
  220.             destination_keyword='dest', interface_keyword='interface',
  221.             member_keyword='member', path_keyword='path')
  222.  
  223.  
  224.     def initPixmaps(self):
  225.         self.func_icons_cached = False
  226.         self.func_icons = {}
  227.         self.device_icons = {}
  228.  
  229.          # Application icon
  230.         self.setWindowIcon(QIcon(load_pixmap('prog', '48x48')))
  231.  
  232.         self.fax_icon = load_pixmap("fax2", "other")
  233.  
  234.  
  235.     def initUI(self):
  236.         # Setup device icon list
  237.         self.DeviceList.setSortingEnabled(True)
  238.  
  239.         # Setup main menu
  240.         self.DeviceRefreshAction.setIcon(QIcon(load_pixmap("refresh1", "16x16")))
  241.         self.connect(self.DeviceRefreshAction, SIGNAL("triggered()"), self.DeviceRefreshAction_activated)
  242.  
  243.         self.RefreshAllAction.setIcon(QIcon(load_pixmap("refresh", "16x16")))
  244.         self.connect(self.RefreshAllAction, SIGNAL("triggered()"), self.RefreshAllAction_activated)
  245.  
  246.         self.SetupDeviceAction.setIcon(QIcon(load_pixmap('list_add', '16x16')))
  247.         self.connect(self.SetupDeviceAction, SIGNAL("triggered()"), self.SetupDeviceAction_activated)
  248.  
  249.         self.RemoveDeviceAction.setIcon(QIcon(load_pixmap('list_remove', '16x16')))
  250.         self.connect(self.RemoveDeviceAction, SIGNAL("triggered()"), self.RemoveDeviceAction_activated)
  251.  
  252.         self.PreferencesAction.setIcon(QIcon(load_pixmap('settings', '16x16')))
  253.         self.connect(self.PreferencesAction, SIGNAL("triggered()"), self.PreferencesAction_activated)
  254.  
  255.         self.ContentsAction.setIcon(QIcon(load_pixmap("help", "16x16")))
  256.         self.connect(self.ContentsAction, SIGNAL("triggered()"), self.helpContents)
  257.  
  258.         self.QuitAction.setIcon(QIcon(load_pixmap("quit", "16x16")))
  259.         self.connect(self.QuitAction, SIGNAL("triggered()"), self.quit)
  260.  
  261.         self.connect(self.AboutAction, SIGNAL("triggered()"), self.helpAbout)
  262.  
  263.  
  264.          # Init tabs/controls
  265.         self.initActionsTab()
  266.         self.initStatusTab()
  267.         self.initSuppliesTab()
  268.         self.initPrintSettingsTab()
  269.         self.initPrintControlTab()
  270.  
  271.         self.connect(self.Tabs,SIGNAL("currentChanged(int)"),self.Tabs_currentChanged)
  272.  
  273.         # Resize the splitter so that the device list starts as a single column
  274.         self.splitter.setSizes([80, 600])
  275.  
  276.         # Setup the Device List
  277.         self.DeviceList.setIconSize(QSize(60, 60))
  278.         self.connect(self.DeviceList,  SIGNAL("currentItemChanged(QListWidgetItem * ,QListWidgetItem *)"),
  279.                      self.DeviceList_currentChanged)
  280.  
  281.  
  282.     def initMisc(self):
  283.         self.TabIndex = { 0: self.updateActionsTab,
  284.                           1: self.updateStatusTab,
  285.                           2: self.updateSuppliesTab,
  286.                           3: self.updatePrintSettingsTab,
  287.                           4: self.updatePrintControlTab,
  288.                         }
  289.  
  290.         # docs
  291.         self.docs = "http://hplip.sf.net"
  292.  
  293.         if prop.doc_build:
  294.             g = os.path.join(sys_conf.get('dirs', 'doc'), 'index.html')
  295.             if os.path.exists(g):
  296.                 self.docs = "file://%s" % g
  297.  
  298.         # support
  299.         self.support = "https://launchpad.net/hplip"
  300.  
  301.  
  302.  
  303.     def initalUpdate(self):
  304.         if self.init_failed:
  305.             self.close()
  306.             return
  307.  
  308.         self.rescanDevices()
  309.  
  310.         cont = True
  311.         if self.initial_device_uri is not None:
  312.             if not self.activateDevice(self.initial_device_uri):
  313.                 log.error("Device %s not found" % self.initial_device_uri)
  314.                 cont = False
  315.  
  316.         if self.cur_printer:
  317.             self.getPrinterState()
  318.  
  319.             if self.printer_state == cups.IPP_PRINTER_STATE_STOPPED:
  320.                 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_STOPPED, self.cur_printer)
  321.  
  322.             if not self.printer_accepting:
  323.                 self.cur_device.sendEvent(EVENT_PRINTER_QUEUE_REJECTING_JOBS, self.cur_printer)
  324.  
  325.  
  326.     def activateDevice(self, device_uri):
  327.         log.debug(log.bold("Activate: %s %s %s" % ("*"*20, device_uri, "*"*20)))
  328.         index = 0
  329.         d = self.DeviceList.item(index) #firstItem()
  330.         found = False
  331.  
  332.         while d is not None:
  333.             if d.device_uri == device_uri:
  334.                 found = True
  335.                 self.DeviceList.setSelected(d, True)
  336.                 self.DeviceList.setCurrentItem(d)
  337.                 break
  338.  
  339.             index += 1
  340.             d = self.DeviceList.item(index)
  341.  
  342.         return found
  343.  
  344.  
  345.  
  346.     # ***********************************************************************************
  347.     #
  348.     # UPDATES/NOTIFICATIONS
  349.     #
  350.     # ***********************************************************************************
  351.  
  352.     def handleSessionSignal(self, *args, **kwds):
  353.         if kwds['interface'] == 'com.hplip.Toolbox' and \
  354.             kwds['member'] == 'Event':
  355.  
  356.             log.debug("Handling event...")
  357.             event = device.Event(*args[:6])
  358.             event.debug()
  359.  
  360.             if event.event_code < EVENT_MIN_USER_EVENT:
  361.                 pass
  362.  
  363.             elif event.event_code == EVENT_DEVICE_UPDATE_REPLY:
  364.                 log.debug("EVENT_DEVICE_UPDATE_REPLY (%s)" % event.device_uri)
  365.                 dev = self.findDeviceByURI(event.device_uri)
  366.  
  367.                 if dev is not None:
  368.                     try:
  369.                         self.service.GetStatus(event.device_uri, reply_handler=self.handleStatusReply,
  370.                             error_handler=self.handleStatusError)
  371.  
  372.                     except dbus.exceptions.DBusException, e:
  373.                         log.error("dbus call to GetStatus() failed.")
  374.  
  375.             elif event.event_code == EVENT_USER_CONFIGURATION_CHANGED:
  376.                 log.debug("EVENT_USER_CONFIGURATION_CHANGED")
  377.                 self.user_settings.load()
  378.  
  379.             elif event.event_code == EVENT_HISTORY_UPDATE:
  380.                 log.debug("EVENT_HISTORY_UPDATE (%s)" % event.device_uri)
  381.                 dev = self.findDeviceByURI(event.device_uri)
  382.                 if dev is not None:
  383.                     self.updateHistory(dev)
  384.  
  385.             elif event.event_code == EVENT_SYSTEMTRAY_EXIT:
  386.                 log.debug("EVENT_SYSTEMTRAY_EXIT")
  387.                 log.error("HPLIP Status Service was closed. HPLIP Device Manager will now exit.")
  388.                 self.close()
  389.  
  390.             elif event.event_code == EVENT_RAISE_DEVICE_MANAGER:
  391.                 log.debug("EVENT_RAISE_DEVICE_MANAGER")
  392.                 self.showNormal()
  393.                 self.setWindowState(self.windowState() & ~Qt.WindowMinimized | Qt.WindowActive)
  394.                 self.raise_()
  395.  
  396.             elif event.event_code in (EVENT_DEVICE_START_POLLING,
  397.                                       EVENT_DEVICE_STOP_POLLING,
  398.                                       EVENT_POLLING_REQUEST):
  399.                 pass
  400.  
  401.             else:
  402.                 log.error("Unhandled event: %d" % event.event_code)
  403.  
  404.  
  405.     def handleStatusReply(self, device_uri, data):
  406.         dev = self.findDeviceByURI(device_uri)
  407.         if dev is not None:
  408.             t = {}
  409.             for key in data:
  410.                 value = model_obj.convert_data(str(key), str(data[key]))
  411.                 t.setdefault(key, value)
  412.  
  413.             dev.dq = t.copy()
  414.             for d in dev.dq:
  415.                 dev.__dict__[d.replace('-','_')] = dev.dq[d]
  416.  
  417.             self.updateDevice(dev)
  418.  
  419.  
  420.     def handleStatusError(self, e):
  421.         log.error(str(e))
  422.  
  423.  
  424.     def updateHistory(self, dev=None):
  425.         if dev is None:
  426.             dev = self.cur_device
  427.  
  428.         try:
  429.             self.service.GetHistory(dev.device_uri, reply_handler=self.handleHistoryReply,
  430.                                     error_handler=self.handleHistoryError)
  431.         except dbus.exceptions.DBusException, e:
  432.             log.error("dbus call to GetHistory() failed.")
  433.  
  434.  
  435.     def handleHistoryReply(self, device_uri, history):
  436.         dev = self.findDeviceByURI(device_uri)
  437.         if dev is not None:
  438.             result = []
  439.             history.reverse()
  440.  
  441.             for h in history:
  442.                 result.append(device.Event(*tuple(h)))
  443.  
  444.             try:
  445.                 self.error_code = result[0].event_code
  446.             except IndexError:
  447.                 self.error_code = STATUS_UNKNOWN
  448.  
  449.             dev.error_state = STATUS_TO_ERROR_STATE_MAP.get(self.error_code, ERROR_STATE_CLEAR)
  450.             dev.hist = result
  451.  
  452.             self.updateDevice(dev)
  453.  
  454.  
  455.     def handleHistoryError(self, e):
  456.         log.error(str(e))
  457.  
  458.  
  459.     def sendMessage(self, device_uri, printer_name, event_code, username=prop.username,
  460.                     job_id=0, title=''):
  461.  
  462.         device.Event(device_uri, printer_name, event_code, username,
  463.                     job_id, title).send_via_dbus(self.session_bus)
  464.  
  465.  
  466.     def timedRefresh(self):
  467.         if not self.updating and self.user_settings.auto_refresh and self.allow_auto_refresh:
  468.             log.debug("Refresh timer...")
  469.             self.cleanupChildren()
  470.  
  471.             if self.user_settings.auto_refresh_type == 0:
  472.                 self.requestDeviceUpdate()
  473.             else:
  474.                 self.rescanDevices()
  475.  
  476.  
  477.     # ***********************************************************************************
  478.     #
  479.     # TAB/DEVICE CHANGE SLOTS
  480.     #
  481.     # ***********************************************************************************
  482.  
  483.     def Tabs_currentChanged(self, tab=0):
  484.         """ Called when the active tab changes.
  485.             Update newly displayed tab.
  486.         """
  487.         if self.cur_device is not None:
  488.             self.TabIndex[tab]()
  489.  
  490.     def updateAllTabs(self):
  491.         for tab in self.TabIndex:
  492.             self.TabIndex[tab]()
  493.  
  494.  
  495.     def updateCurrentTab(self):
  496.         log.debug("updateCurrentTab()")
  497.         self.TabIndex[self.Tabs.currentIndex()]()
  498.  
  499.  
  500.  
  501.     # ***********************************************************************************
  502.     #
  503.     # DEVICE ICON LIST/DEVICE UPDATE(S)
  504.     #
  505.     # ***********************************************************************************
  506.  
  507.  
  508.     def DeviceRefreshAction_activated(self):
  509.         self.DeviceRefreshAction.setEnabled(False)
  510.         self.requestDeviceUpdate()
  511.         self.DeviceRefreshAction.setEnabled(True)
  512.  
  513.  
  514.     def RefreshAllAction_activated(self):
  515.         self.rescanDevices()
  516.  
  517.  
  518.     def createDeviceIcon(self, dev=None):
  519.         if dev is None:
  520.             dev = self.cur_device
  521.  
  522.         try:
  523.             dev.icon
  524.         except AttributeError:
  525.             dev.icon = "default_printer"
  526.  
  527.         try:
  528.             self.device_icons[dev.icon]
  529.         except:
  530.             self.device_icons[dev.icon] = load_pixmap(dev.icon, 'devices')
  531.  
  532.         pix = self.device_icons[dev.icon]
  533.  
  534.         w, h = pix.width(), pix.height()
  535.         error_state = dev.error_state
  536.         icon = QPixmap(w, h)
  537.         p = QPainter(icon)
  538.         p.eraseRect(0, 0, icon.width(), icon.height())
  539.         p.drawPixmap(0, 0, pix)
  540.  
  541.         try:
  542.             tech_type = dev.tech_type
  543.         except AttributeError:
  544.             tech_type = TECH_TYPE_NONE
  545.  
  546.         if dev.device_type == DEVICE_TYPE_FAX:
  547.             p.drawPixmap(w - self.fax_icon.width(), 0, self.fax_icon)
  548.  
  549.         if error_state != ERROR_STATE_CLEAR:
  550.             if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
  551.                 status_icon = getStatusOverlayIcon(error_state)[0] # ink
  552.             else:
  553.                 status_icon = getStatusOverlayIcon(error_state)[1] # laser
  554.  
  555.             if status_icon is not None:
  556.                 p.drawPixmap(0, 0, status_icon)
  557.  
  558.         p.end()
  559.         return icon
  560.  
  561.  
  562.     def refreshDeviceList(self):
  563.         global devices
  564.         log.debug("Rescanning device list...")
  565.  
  566.         if 1:
  567.             beginWaitCursor()
  568.             self.updating = True
  569.  
  570.             self.setWindowTitle(self.__tr("Refreshing Device List - HP Device Manager"))
  571.             self.statusBar().showMessage(self.__tr("Refreshing device list..."))
  572.  
  573.             self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
  574.  
  575.             current = None
  576.  
  577.             try:
  578.                 adds = []
  579.                 for d in self.cups_devices:
  580.                     if d not in device_list:
  581.                         adds.append(d)
  582.  
  583.                 log.debug("Adds: %s" % ','.join(adds))
  584.  
  585.                 removals = []
  586.                 for d in device_list:
  587.                     if d not in self.cups_devices:
  588.                         removals.append(d)
  589.  
  590.                 log.debug("Removals (1): %s" % ','.join(removals))
  591.  
  592.                 updates = []
  593.                 for d in device_list:
  594.                     if d not in adds and d not in removals:
  595.                         updates.append(d)
  596.  
  597.                 log.debug("Updates: %s" % ','.join(updates))
  598.  
  599.                 for d in adds:
  600.                     log.debug("adding: %s" % d)
  601.                     # Note: Do not perform any I/O with this device.
  602.                     dev = device.Device(d, service=self.service, disable_dbus=False)
  603.  
  604.                     if not dev.supported:
  605.                         log.debug("Unsupported model - removing device.")
  606.                         removals.append(d)
  607.                         continue
  608.  
  609.                     icon = self.createDeviceIcon(dev)
  610.  
  611.                     if dev.device_type == DEVICE_TYPE_FAX:
  612.                         DeviceViewItem(self.DeviceList,  self.__tr("%1 (Fax)").arg(dev.model_ui),
  613.                             icon, d)
  614.                     else:
  615.                         if dev.fax_type:
  616.                             DeviceViewItem(self.DeviceList, self.__tr("%1 (Printer)").arg(dev.model_ui),
  617.                                 icon, d)
  618.                         else:
  619.                             DeviceViewItem(self.DeviceList, dev.model_ui,
  620.                                 icon, d)
  621.  
  622.                     device_list[d] = dev
  623.  
  624.                 log.debug("Removals (2): %s" % ','.join(removals))
  625.  
  626.                 for d in removals:
  627.                     index = self.DeviceList.count()-1
  628.                     item = self.DeviceList.item(index)
  629.                     log.debug("removing: %s" % d)
  630.  
  631.                     try:
  632.                         del device_list[d]
  633.                     except KeyError:
  634.                         pass
  635.  
  636.                     while index >= 0 and item is not None:
  637.                         if item.device_uri == d:
  638.                             self.DeviceList.takeItem(index)
  639.                             break
  640.  
  641.                         index -= 1
  642.                         item = self.DeviceList.item(index)
  643.  
  644.                     qApp.processEvents()
  645.  
  646.                 self.DeviceList.updateGeometry()
  647.                 qApp.processEvents()
  648.  
  649.                 if len(device_list):
  650.                     for tab in self.TabIndex:
  651.                         self.Tabs.setTabEnabled(tab, True)
  652.  
  653.                     if self.cur_device_uri:
  654.                         index = 0
  655.                         item = first_item = self.DeviceList.item(index)
  656.  
  657.                         while item is not None:
  658.                             qApp.processEvents()
  659.                             if item.device_uri == self.cur_device_uri:
  660.                                 current = item
  661.                                 self.statusBar().showMessage(self.cur_device_uri)
  662.                                 break
  663.  
  664.                             index += 1
  665.                             item = self.DeviceList.item(index)
  666.  
  667.                         else:
  668.                             self.cur_device = None
  669.                             self.cur_device_uri = ''
  670.  
  671.                     if self.cur_device is None:
  672.                         i = self.DeviceList.item(0)
  673.                         if i is not None:
  674.                             self.cur_device_uri = i.device_uri
  675.                             self.cur_device = device_list[self.cur_device_uri]
  676.                             current = i
  677.  
  678.                     self.updatePrinterCombos()
  679.  
  680.                     if self.cur_device_uri:
  681.                         user_conf.set('last_used', 'device_uri',self.cur_device_uri)
  682.  
  683.                     for d in updates + adds:
  684.                         if d not in removals:
  685.                             self.requestDeviceUpdate(device_list[d])
  686.  
  687.                 else: # no devices
  688.                     self.cur_device = None
  689.                     self.DeviceRefreshAction.setEnabled(False)
  690.                     self.RemoveDeviceAction.setEnabled(False)
  691.                     self.updating = False
  692.                     self.statusBar().showMessage(self.__tr("Press F6 to refresh."))
  693.  
  694.                     for tab in self.TabIndex:
  695.                         self.Tabs.setTabEnabled(tab, False)
  696.  
  697.                     endWaitCursor()
  698.  
  699.                     dlg = NoDevicesDialog(self)
  700.                     dlg.exec_()
  701.  
  702.             finally:
  703.                 self.updating = False
  704.                 endWaitCursor()
  705.  
  706.             if current is not None:
  707.                 self.DeviceList.setCurrentItem(current)
  708.  
  709.             self.DeviceRefreshAction.setEnabled(True)
  710.  
  711.             if self.cur_device is not None:
  712.                 self.RemoveDeviceAction.setEnabled(True)
  713.  
  714.                 self.statusBar().showMessage(self.cur_device_uri)
  715.                 self.updateWindowTitle()
  716.  
  717.  
  718.  
  719.     def updateWindowTitle(self):
  720.         if self.cur_device.device_type == DEVICE_TYPE_FAX:
  721.                 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Fax)").arg(self.cur_device.model_ui))
  722.         else:
  723.             if self.cur_device.fax_type:
  724.                 self.setWindowTitle(self.__tr("HP Device Manager - %1 (Printer)").arg(self.cur_device.model_ui))
  725.             else:
  726.                 self.setWindowTitle(self.__tr("HP Device Manager - %1").arg(self.cur_device.model_ui))
  727.  
  728.         self.statusBar().showMessage(self.cur_device_uri)
  729.  
  730.  
  731.     def updateDeviceByURI(self, device_uri):
  732.         return self.updateDevice(self.findDeviceByURI(device_uri))
  733.  
  734.  
  735.     def updateDevice(self, dev=None, update_tab=True):
  736.  
  737.         """ Update the device icon and currently displayed tab.
  738.         """
  739.         if dev is None:
  740.             dev = self.cur_device
  741.  
  742.         log.debug("updateDevice(%s)" % dev.device_uri)
  743.  
  744.         item = self.findItem(dev)
  745.  
  746.         if item is not None:
  747.             item.setIcon(QIcon(self.createDeviceIcon(dev)))
  748.  
  749.         if dev is self.cur_device and update_tab:
  750.             self.updatePrinterCombos()
  751.             self.updateCurrentTab()
  752.             self.statusBar().showMessage(self.cur_device_uri)
  753.  
  754.  
  755.     def DeviceList_currentChanged(self, i,  j):
  756.         if i is not None and not self.updating:
  757.             self.cur_device_uri = self.DeviceList.currentItem().device_uri
  758.             self.cur_device = device_list[self.cur_device_uri]
  759.             user_conf.set('last_used', 'device_uri', self.cur_device_uri)
  760.  
  761.             self.updateDevice()
  762.             self.updateWindowTitle()
  763.  
  764.  
  765.     def findItem(self, dev):
  766.         if dev is None:
  767.             dev = self.cur_device
  768.  
  769.         return self.findItemByURI(dev.device_uri)
  770.  
  771.  
  772.     def findItemByURI(self, device_uri):
  773.         index = 0
  774.         item = self.DeviceList.item(index)
  775.  
  776.         while item is not None:
  777.             if item.device_uri == device_uri:
  778.                 return item
  779.  
  780.             index += 1
  781.             item = self.DeviceList.item(index)
  782.  
  783.  
  784.     def findDeviceByURI(self, device_uri):
  785.         try:
  786.             return device_list[device_uri]
  787.         except:
  788.             return None
  789.  
  790.  
  791.     def requestDeviceUpdate(self, dev=None, item=None):
  792.         """ Submit device update request to update thread. """
  793.  
  794.         if dev is None:
  795.             dev = self.cur_device
  796.  
  797.         if dev is not None:
  798.             dev.error_state = ERROR_STATE_REFRESHING
  799.             self.updateDevice(dev, update_tab=False)
  800.  
  801.             self.sendMessage(dev.device_uri, '', EVENT_DEVICE_UPDATE_REQUESTED)
  802.  
  803.  
  804.     def rescanDevices(self):
  805.         """ Rescan and update all devices. """
  806.         if not self.updating:
  807.             self.RefreshAllAction.setEnabled(False)
  808.             try:
  809.                 self.refreshDeviceList()
  810.             finally:
  811.                 self.RefreshAllAction.setEnabled(True)
  812.  
  813.  
  814.     def callback(self):
  815.         qApp.processEvents()
  816.  
  817.  
  818.     # ***********************************************************************************
  819.     #
  820.     # DEVICE LIST RIGHT CLICK
  821.     #
  822.     # ***********************************************************************************
  823.  
  824.     def DeviceList_rightButtonClicked(self, item, pos):
  825.         popup = QPopupMenu(self)
  826.  
  827.         if item is not None and item is self.DeviceList.currentItem():
  828.             if self.cur_device.error_state != ERROR_STATE_ERROR:
  829.                 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  830.                     popup.insertItem(self.__tr("Print..."), self.PrintButton_clicked)
  831.  
  832.                     if self.cur_device.scan_type:
  833.                         popup.insertItem(self.__tr("Scan..."), self.ScanButton_clicked)
  834.  
  835.                     if self.cur_device.pcard_type:
  836.                         popup.insertItem(self.__tr("Access Photo Cards..."), self.PCardButton_clicked)
  837.  
  838.                     if self.cur_device.copy_type:
  839.                         popup.insertItem(self.__tr("Make Copies..."), self.MakeCopiesButton_clicked)
  840.  
  841.                 elif self.cur_device.device_type == DEVICE_TYPE_FAX:
  842.                     if self.cur_device.fax_type:
  843.                         popup.insertItem(self.__tr("Send Fax..."), self.SendFaxButton_clicked)
  844.  
  845.                 popup.insertSeparator()
  846.  
  847.             if not self.updating:
  848.                 popup.insertItem(self.__tr("Refresh Device"), self.DeviceRefreshAction_activated)
  849.  
  850.         if not self.updating:
  851.             popup.insertItem(self.__tr("Refresh All"), self.RefreshAllAction_activated)
  852.  
  853.         popup.popup(pos)
  854.  
  855.  
  856.     # ***********************************************************************************
  857.     #
  858.     # PRINTER NAME COMBOS
  859.     #
  860.     # ***********************************************************************************
  861.  
  862.  
  863.     def updatePrinterCombos(self):
  864.         self.PrintSettingsPrinterNameCombo.clear()
  865.         self.PrintControlPrinterNameCombo.clear()
  866.  
  867.         if self.cur_device is not None and \
  868.             self.cur_device.supported:
  869.  
  870.             for c in self.cur_device.cups_printers:
  871.                 self.PrintSettingsPrinterNameCombo.insertItem(0, c.decode("utf-8"))
  872.                 self.PrintControlPrinterNameCombo.insertItem(0, c.decode("utf-8"))
  873.  
  874.             self.cur_printer = unicode(self.PrintSettingsPrinterNameCombo.currentText())
  875.  
  876.  
  877.     def PrintSettingsPrinterNameCombo_activated(self, s):
  878.         self.cur_printer = unicode(s)
  879.         self.PrintControlPrinterNameCombo.setCurrentText(self.cur_printer.encode("latin1")) # TODO: ?
  880.         return self.PrinterCombo_activated(self.cur_printer)
  881.  
  882.  
  883.     def PrintControlPrinterNameCombo_activated(self, s):
  884.         self.cur_printer = unicode(s)
  885.         self.PrintSettingsPrinterNameCombo.setCurrentText(self.cur_printer.encode("latin1")) # TODO: ?
  886.         return self.PrinterCombo_activated(self.cur_printer)
  887.  
  888.  
  889.     def PrinterCombo_activated(self, printer):
  890.         self.updateCurrentTab()
  891.  
  892.  
  893.  
  894.     # ***********************************************************************************
  895.     #
  896.     # FUNCTIONS/ACTION TAB
  897.     #
  898.     # ***********************************************************************************
  899.  
  900.     def initActionsTab(self):
  901.         self.click_lock = None
  902.         self.ActionsList.setIconSize(QSize(32, 32))
  903.         self.connect(self.ActionsList, SIGNAL("itemClicked(QListWidgetItem *)"),  self.ActionsList_clicked)
  904.         self.connect(self.ActionsList, SIGNAL("itemDoubleClicked(QListWidgetItem *)"),  self.ActionsList_clicked)
  905.  
  906.  
  907.     def updateActionsTab(self):
  908.         beginWaitCursor()
  909.         try:
  910.             self.ActionsList.clear()
  911.  
  912.             d = self.cur_device
  913.  
  914.             if d is not None:
  915.                 avail = d.device_state != DEVICE_STATE_NOT_FOUND and d.supported
  916.                 fax = d.fax_type and prop.fax_build and d.device_type == DEVICE_TYPE_FAX and \
  917.                     sys.hexversion >= 0x020300f0 and avail
  918.                 printer = d.device_type == DEVICE_TYPE_PRINTER and avail
  919.                 req_plugin = d.plugin == PLUGIN_REQUIRED
  920.                 opt_plugin = d.plugin == PLUGIN_OPTIONAL
  921.  
  922.                 hplip_conf = ConfigParser.ConfigParser()
  923.                 fp = open("/etc/hp/hplip.conf", "r")
  924.                 hplip_conf.readfp(fp)
  925.                 fp.close()
  926.  
  927.                 try:
  928.                     plugin_installed = utils.to_bool(hplip_conf.get("hplip", "plugin"))
  929.                 except ConfigParser.NoOptionError:
  930.                     plugin_installed = False
  931.  
  932.                 if d.plugin != PLUGIN_NONE:
  933.                     if req_plugin and plugin_installed:
  934.                         x = self.__tr("Download and install<br>required plugin (already installed).")
  935.  
  936.                     elif req_plugin and not plugin_installed:
  937.                         x = self.__tr("Download and install<br>required plugin (needs installation).")
  938.  
  939.                     elif opt_plugin and plugin_installed:
  940.                         x = self.__tr("Download and install<br>optional plugin (already installed).")
  941.  
  942.                     elif opt_plugin and not plugin_installed:
  943.                         x = self.__tr("Download and install<br>optional plugin (needs installation).")
  944.  
  945.                 else:
  946.                     x = ''
  947.  
  948.                 # TODO: Cache this data structure
  949.                 #       -- add a field that specifies if the icon should always show, or only when device is avail.
  950.                 # TODO: Tooltips
  951.                 # TODO: Right-click icon/list view menu
  952.  
  953.                 self.ICONS = [
  954.  
  955.                     # PRINTER
  956.  
  957.                     (lambda : printer,
  958.                     self.__tr("Print"),                        # Text
  959.                     "print",                                   # Icon
  960.                     self.__tr("Print documents or files."),    # Tooltip
  961.                     lambda : PrintDialog(self, self.cur_printer)),  # command/action
  962.  
  963.                     (lambda : d.scan_type and prop.scan_build and \
  964.                         d.device_type == DEVICE_TYPE_PRINTER and avail and \
  965.                         self.user_settings.cmd_scan,
  966.                     self.__tr("Scan"),
  967.                     "scan",
  968.                     self.__tr("Scan a document, image, or photograph.<br>"),
  969.                     self.user_settings.cmd_scan),
  970.  
  971.                     (lambda : d.copy_type and d.device_type == DEVICE_TYPE_PRINTER and \
  972.                         avail,
  973.                     self.__tr("Make Copies"),
  974.                     "makecopies",
  975.                     self.__tr("Make copies on the device controlled by the PC.<br>"),
  976.                     lambda : MakeCopiesDialog(self, self.cur_device_uri)),
  977.  
  978.                     # FAX
  979.  
  980.                     (lambda: fax,
  981.                     self.__tr("Send Fax"),
  982.                     "fax",
  983.                     self.__tr("Send a fax from the PC."),
  984.                     lambda : SendFaxDialog(self, self.cur_printer, self.cur_device_uri)),
  985.  
  986.                     (lambda: fax,
  987.                     self.__tr("Fax Setup"),
  988.                     "fax_setup",
  989.                     self.__tr("Fax support must be setup before you can send faxes."),
  990.                     lambda : FaxSetupDialog(self, self.cur_device_uri)),
  991.  
  992.                     (lambda: fax and self.user_settings.cmd_fab,
  993.                     self.__tr("Fax Address Book"),
  994.                     "fab",
  995.                     self.__tr("Setup fax phone numbers to use when sending faxes from the PC."),
  996.                     self.user_settings.cmd_fab),
  997.  
  998.                     # SETTINGS/TOOLS
  999.  
  1000.                     (lambda : d.power_settings != POWER_SETTINGS_NONE and avail,
  1001.                     self.__tr("Device Settings"),
  1002.                     "settings",
  1003.                     self.__tr("Your device has special device settings.<br>You may alter these settings here."),
  1004.                     lambda : DeviceSetupDialog(self, self.cur_device_uri)),
  1005.  
  1006.                     (lambda : printer,
  1007.                     self.__tr("Print Test Page"),
  1008.                     "testpage",
  1009.                     self.__tr("Print a test page to test the setup of your printer."),
  1010.                     lambda : PrintTestPageDialog(self, self.cur_printer)),
  1011.  
  1012.                     (lambda : True,
  1013.                     self.__tr("View Printer and Device Information"),
  1014.                     "cups",
  1015.                     self.__tr("View information about the device and all its CUPS queues."),
  1016.                     lambda : InfoDialog(self, self.cur_device_uri)),
  1017.  
  1018.                     (lambda: printer and d.align_type != ALIGN_TYPE_NONE,
  1019.                     self.__tr("Align Cartridges (Print Heads)"),
  1020.                     "align",
  1021.                     self.__tr("This will improve the quality of output when a new cartridge is installed."),
  1022.                     lambda : AlignDialog(self, self.cur_device_uri)),
  1023.  
  1024.                     (lambda: printer and d.clean_type != CLEAN_TYPE_NONE,
  1025.                     self.__tr("Clean Cartridges"),
  1026.                     "clean",
  1027.                     self.__tr("You only need to perform this action if you are<br>having problems with poor printout quality due to clogged ink nozzles."),
  1028.                     lambda : CleanDialog(self, self.cur_device_uri)),
  1029.  
  1030.                     (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type == COLOR_CAL_TYPE_TYPHOON,
  1031.                     self.__tr("Color Calibration"),
  1032.                     "colorcal",
  1033.                     self.__tr("Use this procedure to optimimize your printer's color output<br>(requires glossy photo paper)."),
  1034.                     lambda : ColorCalDialog(self, self.cur_device_uri)),
  1035.  
  1036.                     (lambda: printer and d.color_cal_type != COLOR_CAL_TYPE_NONE and d.color_cal_type != COLOR_CAL_TYPE_TYPHOON,
  1037.                     self.__tr("Color Calibration"),
  1038.                     "colorcal",
  1039.                     self.__tr("Use this procedure to optimimize your printer's color output."),
  1040.                     lambda : ColorCalDialog(self, self.cur_device_uri)),
  1041.  
  1042.                     (lambda: printer and d.linefeed_cal_type != LINEFEED_CAL_TYPE_NONE,
  1043.                     self.__tr("Line Feed Calibration"),
  1044.                     "linefeed_cal",
  1045.                     self.__tr("Use line feed calibration to optimize print quality<br>(to remove gaps in the printed output)."),
  1046.                     lambda : LineFeedCalDialog(self, self.cur_device_uri)),
  1047.  
  1048.                     (lambda: printer and d.pq_diag_type != PQ_DIAG_TYPE_NONE,
  1049.                     self.__tr("Print Diagnostic Page"),
  1050.                     "pq_diag",
  1051.                     self.__tr("Your printer can print a test page <br>to help diagnose print quality problems."),
  1052.                     lambda : PQDiagDialog(self, self.cur_device_uri)),
  1053.  
  1054.                     # FIRMWARE
  1055.  
  1056.                     (lambda : printer and d.fw_download ,
  1057.                     self.__tr("Download Firmware"),
  1058.                     "firmware",
  1059.                     self.__tr("Download firmware to your printer <br>(required on some devices after each power-up)."),
  1060.                     lambda : FirmwareDialog(self, self.cur_device_uri)),
  1061.  
  1062.                     # PLUGIN
  1063.  
  1064.                     (lambda : req_plugin,
  1065.                     self.__tr("Install Required Plugin"),
  1066.                     "plugin",
  1067.                     x,
  1068.                     lambda : PluginInstall(self, d.plugin, plugin_installed)),
  1069.  
  1070.                     (lambda : opt_plugin,
  1071.                     self.__tr("Install Optional Plugin"),
  1072.                     "plugin",
  1073.                     x,
  1074.                     lambda : PluginInstall(self, d.plugin, plugin_installed)),
  1075.  
  1076.                     # HELP/WEBSITE
  1077.  
  1078.                     (lambda : True,
  1079.                     self.__tr("Visit HPLIP Support Website"),
  1080.                     "support2",
  1081.                     self.__tr("Visit HPLIP Support Website."),
  1082.                     self.support),
  1083.  
  1084.                     (lambda : True,
  1085.                     self.__tr("Help"),
  1086.                     "help",
  1087.                     self.__tr("View HPLIP help."),
  1088.                     self.docs),
  1089.  
  1090.                 ]
  1091.  
  1092.                 if not self.func_icons_cached:
  1093.                     for filter, text, icon, tooltip, cmd in self.ICONS:
  1094.                         self.func_icons[icon] = load_pixmap(icon, '32x32')
  1095.                     self.func_icons_cached = True
  1096.  
  1097.                 for filter, text, icon, tooltip, cmd in self.ICONS:
  1098.                     if filter is not None:
  1099.                         if not filter():
  1100.                             continue
  1101.  
  1102.                     FuncViewItem(self.ActionsList, text,
  1103.                         self.func_icons[icon],
  1104.                         tooltip,
  1105.                         cmd)
  1106.         finally:
  1107.             endWaitCursor()
  1108.  
  1109.  
  1110.     def ActionsList_clicked(self, item):
  1111.         if item is not None and self.click_lock is not item:
  1112.             self.click_lock = item
  1113.  
  1114.             if item.cmd and callable(item.cmd):
  1115.  
  1116.                 dlg = item.cmd()
  1117.                 self.sendMessage('', '', EVENT_DEVICE_STOP_POLLING)
  1118.                 try:
  1119.                     dlg.exec_()
  1120.                 finally:
  1121.                     self.sendMessage('', '', EVENT_DEVICE_START_POLLING)
  1122.  
  1123.             else:
  1124.                 beginWaitCursor()
  1125.                 #try:
  1126.                 if 1:
  1127.                     if item.cmd.split(':')[0] in ('http', 'https', 'file'):
  1128.                         log.debug("Opening browser to: %s" % item.cmd)
  1129.                         utils.openURL(item.cmd)
  1130.                     else:
  1131.                         self.runExternalCommand(item.cmd)
  1132.                 #finally:
  1133.                 #    endWaitCursor()
  1134.  
  1135.             #self.click_lock = item
  1136.             QTimer.singleShot(1000, self.unlockClick)
  1137.  
  1138.  
  1139.     def unlockClick(self):
  1140.         self.click_lock = None
  1141.         endWaitCursor()
  1142.  
  1143.  
  1144.     def ActionsList_customContextMenuRequested(self, p):
  1145.         print p
  1146.         #pass
  1147.  
  1148.  
  1149.  
  1150.     # ***********************************************************************************
  1151.     #
  1152.     # STATUS TAB
  1153.     #
  1154.     # ***********************************************************************************
  1155.  
  1156.     def initStatusTab(self):
  1157.         self.StatusTable.setColumnCount(0)
  1158.         self.status_headers = [self.__tr(""), self.__tr("Status"), self.__tr("Date and Time"),
  1159.                                self.__tr("Code"), self.__tr("Job ID"), self.__tr("Description")]
  1160.  
  1161.  
  1162.     def updateStatusTab(self):
  1163.         self.updateStatusLCD()
  1164.         self.updateStatusTable()
  1165.  
  1166.  
  1167.     def updateStatusLCD(self):
  1168.         if self.cur_device is not None and \
  1169.             self.cur_device.hist and \
  1170.             self.cur_device.supported:
  1171.  
  1172.             dq = self.cur_device.dq
  1173.  
  1174.             if dq.get('panel', 0) == 1:
  1175.                 line1 = dq.get('panel-line1', '')
  1176.                 line2 = dq.get('panel-line2', '')
  1177.             else:
  1178.                 try:
  1179.                     line1 = device.queryString(self.cur_device.hist[0].event_code)
  1180.                 except (AttributeError, TypeError):
  1181.                     line1 = ''
  1182.  
  1183.                 line2 = ''
  1184.  
  1185.             self.drawStatusLCD(line1, line2)
  1186.  
  1187.         else:
  1188.             if self.cur_device.status_type == STATUS_TYPE_NONE:
  1189.                 self.drawStatusLCD(self.__tr("Status information not"), self.__tr("available for this device."))
  1190.  
  1191.             elif not self.cur_device.supported:
  1192.                 self.drawStatusLCD(self.__tr("Device not supported."))
  1193.  
  1194.             elif not self.cur_device.hist:
  1195.                 self.drawStatusLCD(self.__tr("No status history available."))
  1196.  
  1197.             else:
  1198.                 self.drawStatusLCD()
  1199.  
  1200.  
  1201.     def drawStatusLCD(self, line1='', line2=''):
  1202.         pm = load_pixmap('panel_lcd', 'other')
  1203.  
  1204.         p = QPainter()
  1205.         p.begin(pm)
  1206.         p.setPen(QColor(0, 0, 0))
  1207.         p.setFont(self.font())
  1208.  
  1209.         x, y_line1, y_line2 = 10, 17, 33
  1210.  
  1211.         # TODO: Scroll long lines
  1212.         if line1:
  1213.             p.drawText(x, y_line1, line1)
  1214.  
  1215.         if line2:
  1216.             p.drawText(x, y_line2, line2)
  1217.  
  1218.         p.end()
  1219.  
  1220.         self.LCD.setPixmap(pm)
  1221.  
  1222.  
  1223.  
  1224.     def updateStatusTable(self):
  1225.         self.StatusTable.clear()
  1226.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1227.  
  1228.         row = 0
  1229.         hist = self.cur_device.hist[:]
  1230.  
  1231.         if hist:
  1232.             self.StatusTable.setRowCount(len(hist))
  1233.             self.StatusTable.setColumnCount(len(self.status_headers))
  1234.             self.StatusTable.setHorizontalHeaderLabels(self.status_headers)
  1235.             self.StatusTable.verticalHeader().hide()
  1236.             self.StatusTable.horizontalHeader().show()
  1237.  
  1238.             hist.reverse()
  1239.             row = len(hist)-1
  1240.  
  1241.             for e in hist:
  1242.                 if e is None:
  1243.                     continue
  1244.  
  1245.                 ess = device.queryString(e.event_code, 0)
  1246.                 esl = device.queryString(e.event_code, 1)
  1247.  
  1248.                 if row == 0:
  1249.                     desc = self.__tr("(most recent)")
  1250.  
  1251.                 else:
  1252.                     desc = getTimeDeltaDesc(e.timedate)
  1253.  
  1254.                 dt = QDateTime()
  1255.                 dt.setTime_t(int(e.timedate)) #, Qt.LocalTime)
  1256.  
  1257.                 # TODO: In Qt4.x, use QLocale.toString(date, format)
  1258.                 tt = QString("%1 %2").arg(dt.toString()).arg(desc)
  1259.  
  1260.                 if e.job_id:
  1261.                     job_id = unicode(e.job_id)
  1262.                 else:
  1263.                     job_id = u''
  1264.  
  1265.                 error_state = STATUS_TO_ERROR_STATE_MAP.get(e.event_code, ERROR_STATE_CLEAR)
  1266.                 tech_type = self.cur_device.tech_type
  1267.  
  1268.                 if tech_type in (TECH_TYPE_COLOR_INK, TECH_TYPE_MONO_INK):
  1269.                     status_pix = getStatusListIcon(error_state)[0] # ink
  1270.                 else:
  1271.                     status_pix = getStatusListIcon(error_state)[1] # laser
  1272.  
  1273.                 event_code = unicode(e.event_code)
  1274.  
  1275.                 i = QTableWidgetItem(QIcon(status_pix), self.__tr(""))
  1276.                 i.setFlags(flags)
  1277.                 self.StatusTable.setItem(row, 0, i)
  1278.  
  1279.                 for col, t in [(1, ess), (2, tt), (3, event_code), (4, job_id), (5, esl)]:
  1280.                     i = QTableWidgetItem(QString(t))
  1281.                     i.setFlags(flags)
  1282.  
  1283.                     self.StatusTable.setItem(row, col, i)
  1284.  
  1285.                 row -= 1
  1286.  
  1287.             self.StatusTable.resizeColumnsToContents()
  1288.             self.StatusTable.setColumnWidth(0, 24)
  1289.  
  1290.         else:
  1291.             self.StatusTable.setRowCount(1)
  1292.             self.StatusTable.setColumnCount(2)
  1293.             self.StatusTable.setHorizontalHeaderLabels(["", ""])
  1294.             self.StatusTable.verticalHeader().hide()
  1295.             self.StatusTable.horizontalHeader().hide()
  1296.  
  1297.             flags = Qt.ItemIsEnabled
  1298.  
  1299.             pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
  1300.             i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1301.             i.setFlags(flags)
  1302.             self.StatusTable.setItem(row, 0, i)
  1303.  
  1304.             i = QTableWidgetItem(self.__tr("Status information not available for this device."))
  1305.             i.setFlags(flags)
  1306.             self.StatusTable.setItem(0, 1, i)
  1307.  
  1308.             self.StatusTable.resizeColumnsToContents()
  1309.             self.StatusTable.setColumnWidth(0, 24)
  1310.  
  1311.  
  1312.     # ***********************************************************************************
  1313.     #
  1314.     # SUPPLIES TAB
  1315.     #
  1316.     # ***********************************************************************************
  1317.  
  1318.     def initSuppliesTab(self):
  1319.         self.pix_battery = load_pixmap('battery', '16x16')
  1320.  
  1321.         yellow = "#ffff00"
  1322.         light_yellow = "#ffffcc"
  1323.         cyan = "#00ffff"
  1324.         light_cyan = "#ccffff"
  1325.         magenta = "#ff00ff"
  1326.         light_magenta = "#ffccff"
  1327.         black = "#000000"
  1328.         blue = "#0000ff"
  1329.         dark_grey = "#808080"
  1330.         light_grey = "#c0c0c0"
  1331.  
  1332.         self.TYPE_TO_PIX_MAP = {
  1333.                                AGENT_TYPE_UNSPECIFIED : [black],
  1334.                                AGENT_TYPE_BLACK: [black],
  1335.                                AGENT_TYPE_CMY: [cyan, magenta, yellow],
  1336.                                AGENT_TYPE_KCM: [light_cyan, light_magenta, light_yellow],
  1337.                                AGENT_TYPE_GGK: [dark_grey],
  1338.                                AGENT_TYPE_YELLOW: [yellow],
  1339.                                AGENT_TYPE_MAGENTA: [magenta],
  1340.                                AGENT_TYPE_CYAN : [cyan],
  1341.                                AGENT_TYPE_CYAN_LOW: [light_cyan],
  1342.                                AGENT_TYPE_YELLOW_LOW: [light_yellow],
  1343.                                AGENT_TYPE_MAGENTA_LOW: [light_magenta],
  1344.                                AGENT_TYPE_BLUE: [blue],
  1345.                                AGENT_TYPE_KCMY_CM: [yellow, cyan, magenta],
  1346.                                AGENT_TYPE_LC_LM: [light_cyan, light_magenta],
  1347.                                #AGENT_TYPE_Y_M: [yellow, magenta],
  1348.                                #AGENT_TYPE_C_K: [black, cyan],
  1349.                                AGENT_TYPE_LG_PK: [light_grey, dark_grey],
  1350.                                AGENT_TYPE_LG: [light_grey],
  1351.                                AGENT_TYPE_G: [dark_grey],
  1352.                                AGENT_TYPE_PG: [light_grey],
  1353.                                AGENT_TYPE_C_M: [cyan, magenta],
  1354.                                AGENT_TYPE_K_Y: [black, yellow],
  1355.                                }
  1356.  
  1357.         self.supplies_headers = [self.__tr(""), self.__tr("Description"),
  1358.                                  self.__tr("HP Part No."), self.__tr("Approx. Level"),
  1359.                                  self.__tr("Status")]
  1360.  
  1361.  
  1362.     def updateSuppliesTab(self):
  1363.         beginWaitCursor()
  1364.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1365.  
  1366.         try:
  1367.             self.SuppliesTable.clear()
  1368.             self.SuppliesTable.setRowCount(0)
  1369.             self.SuppliesTable.setColumnCount(0)
  1370.  
  1371.             if self.cur_device is not None and \
  1372.                 self.cur_device.supported and \
  1373.                 self.cur_device.status_type != STATUS_TYPE_NONE and \
  1374.                 self.cur_device.device_state != DEVICE_STATE_NOT_FOUND:
  1375.  
  1376.                 try:
  1377.                     self.cur_device.sorted_supplies
  1378.                 except AttributeError:
  1379.                     self.cur_device.sorted_supplies = []
  1380.  
  1381.                 if not self.cur_device.sorted_supplies:
  1382.                     a = 1
  1383.                     while True:
  1384.                         try:
  1385.                             agent_type = int(self.cur_device.dq['agent%d-type' % a])
  1386.                             agent_kind = int(self.cur_device.dq['agent%d-kind' % a])
  1387.                         except KeyError:
  1388.                             break
  1389.                         else:
  1390.                             self.cur_device.sorted_supplies.append((a, agent_kind, agent_type))
  1391.  
  1392.                         a += 1
  1393.  
  1394.                     self.cur_device.sorted_supplies.sort(lambda x, y: cmp(x[2], y[2]) or cmp(x[1], y[1]))
  1395.  
  1396.                 self.SuppliesTable.setRowCount(len(self.cur_device.sorted_supplies))
  1397.                 self.SuppliesTable.setColumnCount(len(self.supplies_headers))
  1398.                 self.SuppliesTable.setHorizontalHeaderLabels(self.supplies_headers)
  1399.                 self.SuppliesTable.verticalHeader().hide()
  1400.                 self.SuppliesTable.horizontalHeader().show()
  1401.                 self.SuppliesTable.setIconSize(QSize(100, 18))
  1402.  
  1403.                 for row, x in enumerate(self.cur_device.sorted_supplies):
  1404.                     a, agent_kind, agent_type = x
  1405.                     agent_level = int(self.cur_device.dq['agent%d-level' % a])
  1406.                     agent_sku = str(self.cur_device.dq['agent%d-sku' % a])
  1407.                     agent_desc = self.cur_device.dq['agent%d-desc' % a]
  1408.                     agent_health_desc = self.cur_device.dq['agent%d-health-desc' % a]
  1409.  
  1410.                     # Bar graph level
  1411.                     level_pixmap = None
  1412.                     if agent_kind in (AGENT_KIND_SUPPLY,
  1413.                                       #AGENT_KIND_HEAD,
  1414.                                       AGENT_KIND_HEAD_AND_SUPPLY,
  1415.                                       AGENT_KIND_TONER_CARTRIDGE,
  1416.                                       AGENT_KIND_MAINT_KIT,
  1417.                                       AGENT_KIND_ADF_KIT,
  1418.                                       AGENT_KIND_INT_BATTERY,
  1419.                                       AGENT_KIND_DRUM_KIT,
  1420.                                       ):
  1421.  
  1422.                         level_pixmap = self.createStatusLevelGraphic(agent_level, agent_type)
  1423.  
  1424.                     # Color icon
  1425.                     pixmap = None
  1426.                     if agent_kind in (AGENT_KIND_SUPPLY,
  1427.                                       AGENT_KIND_HEAD,
  1428.                                       AGENT_KIND_HEAD_AND_SUPPLY,
  1429.                                       AGENT_KIND_TONER_CARTRIDGE,
  1430.                                       #AGENT_KIND_MAINT_KIT,
  1431.                                       #AGENT_KIND_ADF_KIT,
  1432.                                       AGENT_KIND_INT_BATTERY,
  1433.                                       #AGENT_KIND_DRUM_KIT,
  1434.                                       ):
  1435.  
  1436.                         pixmap = self.getStatusIcon(agent_kind, agent_type)
  1437.  
  1438.                     if pixmap is not None:
  1439.                         i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1440.                         i.setFlags(flags)
  1441.                         self.SuppliesTable.setItem(row, 0, i)
  1442.  
  1443.                     for col, t in [(1, agent_desc), (2, agent_sku), (4, agent_health_desc)]:
  1444.                         i = QTableWidgetItem(QString(t))
  1445.                         i.setFlags(flags)
  1446.                         self.SuppliesTable.setItem(row, col, i)
  1447.  
  1448.                     if level_pixmap is not None:
  1449.                         i = QTableWidgetItem(QIcon(level_pixmap), self.__tr(""))
  1450.                         i.setFlags(flags)
  1451.                         self.SuppliesTable.setItem(row, 3, i)
  1452.  
  1453.                 self.SuppliesTable.resizeColumnsToContents()
  1454.                 self.SuppliesTable.setColumnWidth(0, 24)
  1455.                 self.SuppliesTable.setColumnWidth(3, 120)
  1456.  
  1457.             else: # No supplies info
  1458.                 log.warning("Supplies information not available for this device.")
  1459.                 flags = Qt.ItemIsEnabled
  1460.                 self.SuppliesTable.setRowCount(1)
  1461.                 self.SuppliesTable.setColumnCount(2)
  1462.                 self.SuppliesTable.setHorizontalHeaderLabels(["", ""])
  1463.                 self.SuppliesTable.verticalHeader().hide()
  1464.                 self.SuppliesTable.horizontalHeader().hide()
  1465.  
  1466.                 i = QTableWidgetItem(self.__tr("Supplies information not available for this device."))
  1467.                 i.setFlags(flags)
  1468.                 self.SuppliesTable.setItem(0, 1, i)
  1469.  
  1470.                 pixmap = getStatusListIcon(ERROR_STATE_ERROR)[0]
  1471.                 i = QTableWidgetItem(QIcon(pixmap), self.__tr(""))
  1472.                 i.setFlags(flags)
  1473.                 self.SuppliesTable.setItem(0, 0, i)
  1474.  
  1475.                 self.SuppliesTable.resizeColumnsToContents()
  1476.                 self.SuppliesTable.setColumnWidth(0, 24)
  1477.  
  1478.         finally:
  1479.             endWaitCursor()
  1480.  
  1481.  
  1482.     def getStatusIcon(self, agent_kind, agent_type):
  1483.         if agent_kind in (AGENT_KIND_SUPPLY,
  1484.                           AGENT_KIND_HEAD,
  1485.                           AGENT_KIND_HEAD_AND_SUPPLY,
  1486.                           AGENT_KIND_TONER_CARTRIDGE):
  1487.  
  1488.             map = self.TYPE_TO_PIX_MAP[agent_type]
  1489.  
  1490.             if isinstance(map, list):
  1491.                 map_len = len(map)
  1492.                 pix = QPixmap(16, 16)
  1493.                 pix.fill(QColor(0, 0, 0, 0))
  1494.                 p = QPainter()
  1495.  
  1496.                 p.begin(pix)
  1497.                 p.setRenderHint(QPainter.Antialiasing)
  1498.  
  1499.                 if map_len == 1:
  1500.                     p.setPen(QColor(map[0]))
  1501.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1502.                     p.drawPie(2, 2, 10, 10, 0, 5760)
  1503.  
  1504.                 elif map_len == 2:
  1505.                     p.setPen(QColor(map[0]))
  1506.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1507.                     p.drawPie(2, 4, 8, 8, 0, 5760)
  1508.  
  1509.                     p.setPen(QColor(map[1]))
  1510.                     p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
  1511.                     p.drawPie(6, 4, 8, 8, 0, 5760)
  1512.  
  1513.                 elif map_len == 3:
  1514.                     p.setPen(QColor(map[2]))
  1515.                     p.setBrush(QBrush(QColor(map[2]), Qt.SolidPattern))
  1516.                     p.drawPie(6, 6, 8, 8, 0, 5760)
  1517.  
  1518.                     p.setPen(QColor(map[1]))
  1519.                     p.setBrush(QBrush(QColor(map[1]), Qt.SolidPattern))
  1520.                     p.drawPie(2, 6, 8, 8, 0, 5760)
  1521.  
  1522.                     p.setPen(QColor(map[0]))
  1523.                     p.setBrush(QBrush(QColor(map[0]), Qt.SolidPattern))
  1524.                     p.drawPie(4, 2, 8, 8, 0, 5760)
  1525.  
  1526.                 p.end()
  1527.                 return pix
  1528.  
  1529.             else:
  1530.                 return map
  1531.  
  1532.         elif agent_kind == AGENT_KIND_INT_BATTERY:
  1533.                 return self.pix_battery
  1534.  
  1535.  
  1536.     def createStatusLevelGraphic(self, percent, agent_type, w=100, h=18):
  1537.         if percent:
  1538.             fw = w/100*percent
  1539.         else:
  1540.             fw = 0
  1541.  
  1542.         px = QPixmap(w, h)
  1543.         px.fill(QColor(0, 0, 0, 0))
  1544.         pp = QPainter()
  1545.         pp.begin(px)
  1546.         pp.setRenderHint(QPainter.Antialiasing)
  1547.         pp.setPen(Qt.black)
  1548.  
  1549.         map = self.TYPE_TO_PIX_MAP[agent_type]
  1550.         map_len = len(map)
  1551.  
  1552.         if map_len == 1 or map_len > 3:
  1553.             pp.fillRect(0, 0, fw, h, QBrush(QColor(map[0])))
  1554.  
  1555.         elif map_len == 2:
  1556.             h2 = h / 2
  1557.             pp.fillRect(0, 0, fw, h2, QBrush(QColor(map[0])))
  1558.             pp.fillRect(0, h2, fw, h, QBrush(QColor(map[1])))
  1559.  
  1560.         elif map_len == 3:
  1561.             h3 = h / 3
  1562.             h23 = 2 * h3
  1563.             pp.fillRect(0, 0, fw, h3, QBrush(QColor(map[0])))
  1564.             pp.fillRect(0, h3, fw, h23, QBrush(QColor(map[1])))
  1565.             pp.fillRect(0, h23, fw, h, QBrush(QColor(map[2])))
  1566.  
  1567.         # draw black frame
  1568.         pp.drawRect(0, 0, w, h)
  1569.  
  1570.         if percent > 75 and agent_type in \
  1571.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1572.             pp.setPen(Qt.white)
  1573.  
  1574.         # 75% ticks
  1575.         w1 = 3 * w / 4
  1576.         h6 = h / 6
  1577.         pp.drawLine(w1, 0, w1, h6)
  1578.         pp.drawLine(w1, h, w1, h-h6)
  1579.  
  1580.         if percent > 50 and agent_type in \
  1581.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1582.             pp.setPen(Qt.white)
  1583.  
  1584.         # 50% ticks
  1585.         w2 = w / 2
  1586.         h4 = h / 4
  1587.         pp.drawLine(w2, 0, w2, h4)
  1588.         pp.drawLine(w2, h, w2, h-h4)
  1589.  
  1590.         if percent > 25 and agent_type in \
  1591.           (AGENT_TYPE_BLACK, AGENT_TYPE_UNSPECIFIED, AGENT_TYPE_BLUE):
  1592.             pp.setPen(Qt.white)
  1593.  
  1594.         # 25% ticks
  1595.         w4 = w / 4
  1596.         pp.drawLine(w4, 0, w4, h6)
  1597.         pp.drawLine(w4, h, w4, h-h6)
  1598.  
  1599.         pp.end()
  1600.  
  1601.         return px
  1602.  
  1603.  
  1604.  
  1605.     # ***********************************************************************************
  1606.     #
  1607.     # PRINTER SETTINGS TAB
  1608.     #
  1609.     # ***********************************************************************************
  1610.  
  1611.     def initPrintSettingsTab(self):
  1612.         pass
  1613.  
  1614.  
  1615.     def updatePrintSettingsTab(self):
  1616.         beginWaitCursor()
  1617.         try:
  1618.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1619.                 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Printer Name:"))
  1620.             else:
  1621.                 self.PrintSettingsPrinterNameLabel.setText(self.__tr("Fax Name:"))
  1622.  
  1623.             self.PrintSettingsToolbox.updateUi(self.cur_device, self.cur_printer)
  1624.         finally:
  1625.             endWaitCursor()
  1626.  
  1627.  
  1628.     # ***********************************************************************************
  1629.     #
  1630.     # PRINTER CONTROL TAB
  1631.     #
  1632.     # ***********************************************************************************
  1633.  
  1634.     def initPrintControlTab(self):
  1635.         self.JOB_STATES = { cups.IPP_JOB_PENDING : self.__tr("Pending"),
  1636.                             cups.IPP_JOB_HELD : self.__tr("On hold"),
  1637.                             cups.IPP_JOB_PROCESSING : self.__tr("Printing"),
  1638.                             cups.IPP_JOB_STOPPED : self.__tr("Stopped"),
  1639.                             cups.IPP_JOB_CANCELLED : self.__tr("Canceled"),
  1640.                             cups.IPP_JOB_ABORTED : self.__tr("Aborted"),
  1641.                             cups.IPP_JOB_COMPLETED : self.__tr("Completed"),
  1642.                            }
  1643.  
  1644.         self.CancelJobButton.setIcon(QIcon(load_pixmap('cancel', '16x16')))
  1645.         self.RefreshButton.setIcon(QIcon(load_pixmap('refresh', '16x16')))
  1646.  
  1647.         self.JOB_STATE_ICONS = { cups.IPP_JOB_PENDING: QIcon(load_pixmap("busy", "16x16")),
  1648.                                  cups.IPP_JOB_HELD : QIcon(load_pixmap("busy", "16x16")),
  1649.                                  cups.IPP_JOB_PROCESSING : QIcon(load_pixmap("print", "16x16")),
  1650.                                  cups.IPP_JOB_STOPPED : QIcon(load_pixmap("warning", "16x16")),
  1651.                                  cups.IPP_JOB_CANCELLED : QIcon(load_pixmap("warning", "16x16")),
  1652.                                  cups.IPP_JOB_ABORTED : QIcon(load_pixmap("error", "16x16")),
  1653.                                  cups.IPP_JOB_COMPLETED : QIcon(load_pixmap("ok", "16x16")),
  1654.                                 }
  1655.  
  1656.         self.connect(self.StartStopButton, SIGNAL("clicked()"), self.StartStopButton_clicked)
  1657.         self.connect(self.AcceptRejectButton, SIGNAL("clicked()"), self.AcceptRejectButton_clicked)
  1658.         self.connect(self.SetDefaultButton, SIGNAL("clicked()"), self.SetDefaultButton_clicked)
  1659.         self.connect(self.CancelJobButton, SIGNAL("clicked()"), self.CancelJobButton_clicked)
  1660.         self.connect(self.RefreshButton, SIGNAL("clicked()"), self.RefreshButton_clicked)
  1661.  
  1662.         self.job_headers = [self.__tr("Status"), self.__tr("Title/Description"), self.__tr("Job ID")]
  1663.  
  1664.         # TODO: Check queues at startup and send events if stopped or rejecting
  1665.  
  1666.  
  1667.     def CancelJobButton_clicked(self):
  1668.         item = self.JobTable.currentItem()
  1669.  
  1670.         if item is not None:
  1671.             job_id, ok = item.data(Qt.UserRole).toInt()
  1672.             if ok and job_id:
  1673.                self.cur_device.cancelJob(job_id)
  1674.                QTimer.singleShot(1000, self.updatePrintControlTab)
  1675.  
  1676.  
  1677.     def RefreshButton_clicked(self):
  1678.         self.updatePrintControlTab()
  1679.  
  1680.  
  1681.     def updatePrintControlTab(self):
  1682.         if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1683.             self.PrintControlPrinterNameLabel.setText(self.__tr("Printer Name:"))
  1684.         else:
  1685.             self.PrintControlPrinterNameLabel.setText(self.__tr("Fax Name:"))
  1686.  
  1687.         self.JobTable.clear()
  1688.         self.JobTable.setRowCount(0)
  1689.         self.JobTable.setColumnCount(0)
  1690.         self.updatePrintController()
  1691.         flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
  1692.         jobs = cups.getJobs()
  1693.         num_jobs = 0
  1694.         for j in jobs:
  1695.             if j.dest.decode('utf-8') == unicode(self.cur_printer):
  1696.                 num_jobs += 1
  1697.  
  1698.         if num_jobs:
  1699.             self.CancelJobButton.setEnabled(True)
  1700.             self.JobTable.setRowCount(num_jobs)
  1701.             self.JobTable.setColumnCount(len(self.job_headers))
  1702.             self.JobTable.setHorizontalHeaderLabels(self.job_headers)
  1703.  
  1704.             for row, j in enumerate(jobs):
  1705.                 if j.dest.decode('utf-8') == unicode(self.cur_printer):
  1706.                     i = QTableWidgetItem(self.JOB_STATE_ICONS[j.state], self.JOB_STATES[j.state])
  1707.                     i.setData(Qt.UserRole, QVariant(j.id))
  1708.                     i.setFlags(flags)
  1709.                     self.JobTable.setItem(row, 0, i)
  1710.  
  1711.                     i = QTableWidgetItem(j.title)
  1712.                     i.setFlags(flags)
  1713.                     self.JobTable.setItem(row, 1, i)
  1714.  
  1715.                     i = QTableWidgetItem(unicode(j.id))
  1716.                     i.setFlags(flags)
  1717.                     self.JobTable.setItem(row, 2, i)
  1718.  
  1719.  
  1720.             self.JobTable.setCurrentCell(0, 0)
  1721.             self.JobTable.resizeColumnsToContents()
  1722.  
  1723.         else:
  1724.             self.CancelJobButton.setEnabled(False)
  1725.  
  1726.  
  1727.     def getPrinterState(self):
  1728.         self.printer_state = cups.IPP_PRINTER_STATE_IDLE
  1729.         self.printer_accepting = True
  1730.         cups_printers = cups.getPrinters()
  1731.  
  1732.         for p in cups_printers:
  1733.             if p.name.decode('utf-8') == self.cur_printer:
  1734.                 self.printer_state = p.state
  1735.                 self.printer_accepting = p.accepting
  1736.                 break
  1737.  
  1738.  
  1739.     def updatePrintController(self):
  1740.         # default printer
  1741.         self.SetDefaultButton.setText(self.__tr("Set as Default"))
  1742.  
  1743.         default_printer = cups.getDefaultPrinter()
  1744.         if default_printer is not None:
  1745.             default_printer = default_printer.decode('utf8')
  1746.  
  1747.         if default_printer == self.cur_printer:
  1748.             self.SetDefaultLabel.setText(self.__tr("Default Printer"))
  1749.             self.SetDefaultIcon.setPixmap(load_pixmap("ok", "16x16"))
  1750.             self.SetDefaultButton.setEnabled(False)
  1751.  
  1752.         else:
  1753.             self.SetDefaultLabel.setText(self.__tr("Not Default Printer"))
  1754.             self.SetDefaultIcon.setPixmap(load_pixmap("info", "16x16"))
  1755.             self.SetDefaultButton.setEnabled(True)
  1756.  
  1757.         self.getPrinterState()
  1758.  
  1759.         # start/stop
  1760.         if self.printer_state == cups.IPP_PRINTER_STATE_IDLE:
  1761.             self.StartStopLabel.setText(self.__tr("Started/Idle"))
  1762.             self.StartStopIcon.setPixmap(load_pixmap("idle", "16x16"))
  1763.  
  1764.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1765.                 self.StartStopButton.setText(self.__tr("Stop Printer"))
  1766.  
  1767.             else:
  1768.                 self.StartStopButton.setText(self.__tr("Stop Fax"))
  1769.  
  1770.         elif self.printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
  1771.             self.StartStopLabel.setText(self.__tr("Started/Processing"))
  1772.             self.StartStopIcon.setPixmap(load_pixmap("busy", "16x16"))
  1773.  
  1774.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1775.                 self.StartStopButton.setText(self.__tr("Stop Printer"))
  1776.  
  1777.             else:
  1778.                 self.StartStopButton.setText(self.__tr("Stop Fax"))
  1779.         else:
  1780.             self.StartStopLabel.setText(self.__tr("Stopped"))
  1781.             self.StartStopIcon.setPixmap(load_pixmap("warning", "16x16"))
  1782.  
  1783.             if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1784.                 self.StartStopButton.setText(self.__tr("Start Printer"))
  1785.  
  1786.             else:
  1787.                 self.StartStopButton.setText(self.__tr("Start Fax"))
  1788.  
  1789.         # reject/accept
  1790.         if self.printer_accepting:
  1791.             self.AcceptRejectLabel.setText(self.__tr("Accepting Jobs"))
  1792.             self.AcceptRejectIcon.setPixmap(load_pixmap("idle", "16x16"))
  1793.             self.AcceptRejectButton.setText(self.__tr("Reject Jobs"))
  1794.  
  1795.         else:
  1796.             self.AcceptRejectLabel.setText(self.__tr("Rejecting Jobs"))
  1797.             self.AcceptRejectIcon.setPixmap(load_pixmap("warning", "16x16"))
  1798.             self.AcceptRejectButton.setText(self.__tr("Accept Jobs"))
  1799.  
  1800.  
  1801.  
  1802.     def StartStopButton_clicked(self):
  1803.         beginWaitCursor()
  1804.         try:
  1805.             if self.printer_state in (cups.IPP_PRINTER_STATE_IDLE, cups.IPP_PRINTER_STATE_PROCESSING):
  1806.                 result = cups.stop(self.cur_printer)
  1807.                 if result:
  1808.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1809.                         e = EVENT_PRINTER_QUEUE_STOPPED
  1810.                     else:
  1811.                         e = EVENT_FAX_QUEUE_STOPPED
  1812.  
  1813.             else:
  1814.                 result = cups.start(self.cur_printer)
  1815.                 if result:
  1816.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1817.                         e = EVENT_PRINTER_QUEUE_STARTED
  1818.                     else:
  1819.                         e = EVENT_FAX_QUEUE_STARTED
  1820.  
  1821.             if result:
  1822.                 self.updatePrintController()
  1823.                 self.cur_device.sendEvent(e, self.cur_printer)
  1824.             else:
  1825.                 log.error("Start/Stop printer operation failed")
  1826.  
  1827.         finally:
  1828.             endWaitCursor()
  1829.  
  1830.  
  1831.  
  1832.     def AcceptRejectButton_clicked(self):
  1833.         beginWaitCursor()
  1834.         try:
  1835.             if self.printer_accepting:
  1836.                 result = cups.reject(self.cur_printer)
  1837.                 if result:
  1838.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1839.                         e = EVENT_PRINTER_QUEUE_REJECTING_JOBS
  1840.                     else:
  1841.                         e = EVENT_FAX_QUEUE_REJECTING_JOBS
  1842.  
  1843.             else:
  1844.                 result = cups.accept(self.cur_printer)
  1845.                 if result:
  1846.                     if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1847.                         e = EVENT_PRINTER_QUEUE_ACCEPTING_JOBS
  1848.                     else:
  1849.                         e = EVENT_FAX_QUEUE_ACCEPTING_JOBS
  1850.  
  1851.             if result:
  1852.                 self.updatePrintController()
  1853.                 self.cur_device.sendEvent(e, self.cur_printer)
  1854.             else:
  1855.                 log.error("Reject/Accept jobs operation failed")
  1856.  
  1857.  
  1858.         finally:
  1859.             endWaitCursor()
  1860.  
  1861.  
  1862.  
  1863.     def SetDefaultButton_clicked(self):
  1864.         beginWaitCursor()
  1865.         try:
  1866.             result = cups.setDefaultPrinter(self.cur_printer.encode('utf8'))
  1867.             if not result:
  1868.                 log.error("Set default printer failed.")
  1869.             else:
  1870.                 self.updatePrintController()
  1871.                 if self.cur_device.device_type == DEVICE_TYPE_PRINTER:
  1872.                     e = EVENT_PRINTER_QUEUE_SET_AS_DEFAULT
  1873.                 else:
  1874.                     e = EVENT_FAX_QUEUE_SET_AS_DEFAULT
  1875.  
  1876.                 self.cur_device.sendEvent(e, self.cur_printer)
  1877.  
  1878.         finally:
  1879.             endWaitCursor()
  1880.  
  1881.  
  1882.  
  1883.     def cancelCheckedJobs(self):
  1884.         beginWaitCursor()
  1885.         try:
  1886.             item = self.JobTable.firstChild()
  1887.             while item is not None:
  1888.                 if item.isOn():
  1889.                     self.cur_device.cancelJob(item.job_id)
  1890.  
  1891.                 item = item.nextSibling()
  1892.  
  1893.         finally:
  1894.             endWaitCursor()
  1895.  
  1896.  
  1897.         self.updatePrintControlTab()
  1898.  
  1899.  
  1900.  
  1901.  
  1902.     # ***********************************************************************************
  1903.     #
  1904.     # EXIT/CHILD CLEANUP
  1905.     #
  1906.     # ***********************************************************************************
  1907.  
  1908.     def closeEvent(self, event):
  1909.         self.cleanup()
  1910.         event.accept()
  1911.  
  1912.  
  1913.     def cleanup(self):
  1914.         self.cleanupChildren()
  1915.  
  1916.  
  1917.     def cleanupChildren(self):
  1918.         log.debug("Cleaning up child processes.")
  1919.         try:
  1920.             os.waitpid(-1, os.WNOHANG)
  1921.         except OSError:
  1922.             pass
  1923.  
  1924.  
  1925.     def quit(self):
  1926.         self.cleanupChildren()
  1927.         self.close()
  1928.  
  1929.  
  1930.     # ***********************************************************************************
  1931.     #
  1932.     # DEVICE SETTINGS PLUGIN
  1933.     #
  1934.     # ***********************************************************************************
  1935.  
  1936.  
  1937.     # ***********************************************************************************
  1938.     #
  1939.     # SETTINGS DIALOG
  1940.     #
  1941.     # ***********************************************************************************
  1942.  
  1943.     def PreferencesAction_activated(self, tab_to_show=0):
  1944.         dlg = SettingsDialog(self)
  1945.         dlg.TabWidget.setCurrentIndex(tab_to_show)
  1946.  
  1947.         if dlg.exec_() == QDialog.Accepted:
  1948.             self.user_settings.load()
  1949.  
  1950.             self.cur_device.sendEvent(EVENT_USER_CONFIGURATION_CHANGED, self.cur_printer)
  1951.  
  1952.  
  1953.     # ***********************************************************************************
  1954.     #
  1955.     # SETUP/REMOVE
  1956.     #
  1957.     # ***********************************************************************************
  1958.  
  1959.     def SetupDeviceAction_activated(self):
  1960.         su_sudo_str = su_sudo()
  1961.         if su_sudo_str is None:
  1962.             QMessageBox.critical(self,
  1963.                 self.windowTitle(),
  1964.                 self.__tr("<b>Unable to find an appropriate su/sudo utility to run hp-setup.</b>"),
  1965.                 QMessageBox.Ok,
  1966.                 QMessageBox.NoButton,
  1967.                 QMessageBox.NoButton)
  1968.  
  1969.         else:
  1970.             if utils.which('hp-setup'):
  1971.                 cmd = su_sudo_str % 'hp-setup -u'
  1972.             else:
  1973.                 cmd = su_sudo_str % 'python ./setup.py -u'
  1974.  
  1975.             log.debug(cmd)
  1976.             utils.run(cmd, log_output=True, password_func=None, timeout=1)
  1977.             self.rescanDevices()
  1978.  
  1979.  
  1980.     def RemoveDeviceAction_activated(self):
  1981.         if self.cur_device is not None:
  1982.             x = QMessageBox.critical(self,
  1983.            self.windowTitle(),
  1984.            self.__tr("<b>Annoying Confirmation: Are you sure you want to remove this device?</b>"),
  1985.             QMessageBox.Yes,
  1986.             QMessageBox.No | QMessageBox.Default,
  1987.             QMessageBox.NoButton)
  1988.             if x == QMessageBox.Yes:
  1989.                 beginWaitCursor()
  1990.                 print_uri = self.cur_device.device_uri
  1991.                 fax_uri = print_uri.replace('hp:', 'hpfax:')
  1992.  
  1993.                 log.debug(print_uri)
  1994.                 log.debug(fax_uri)
  1995.  
  1996.                 self.cups_devices = device.getSupportedCUPSDevices(['hp', 'hpfax'])
  1997.  
  1998.                 for d in self.cups_devices:
  1999.                     if d in (print_uri, fax_uri):
  2000.                         for p in self.cups_devices[d]:
  2001.                             log.debug("Removing %s" % p)
  2002.                             cups.delPrinter(p)
  2003.  
  2004.                 self.cur_device = None
  2005.                 self.cur_device_uri = ''
  2006.                 user_conf.set('last_used', 'device_uri', '')
  2007.                 endWaitCursor()
  2008.  
  2009.                 self.rescanDevices()
  2010.  
  2011.  
  2012.     # ***********************************************************************************
  2013.     #
  2014.     # MISC
  2015.     #
  2016.     # ***********************************************************************************
  2017.  
  2018.     def runExternalCommand(self, cmd, macro_char='%'):
  2019.         beginWaitCursor()
  2020.  
  2021.         try:
  2022.             if len(cmd) == 0:
  2023.                 FailureUI(self.__tr("<p><b>Unable to run command. No command specified.</b><p>Use <pre>Configure...</pre> to specify a command to run."))
  2024.                 log.error("No command specified. Use settings to configure commands.")
  2025.             else:
  2026.                 log.debug("Run: %s %s (%s) %s" % ("*"*20, cmd, self.cur_device_uri, "*"*20))
  2027.                 log.debug(cmd)
  2028.  
  2029.                 try:
  2030.                     cmd = ''.join([self.cur_device.device_vars.get(x, x) \
  2031.            for x in cmd.split(macro_char)])
  2032.                 except AttributeError:
  2033.                     pass
  2034.  
  2035.                 log.debug(cmd)
  2036.  
  2037.                 path = cmd.split()[0]
  2038.                 args = cmd.split()
  2039.  
  2040.                 log.debug(path)
  2041.                 log.debug(args)
  2042.  
  2043.                 self.cleanupChildren()
  2044.                 os.spawnvp(os.P_NOWAIT, path, args)
  2045.                 qApp.processEvents()
  2046.  
  2047.         finally:
  2048.             endWaitCursor()
  2049.  
  2050.  
  2051.     def helpContents(self):
  2052.         utils.openURL(self.docs)
  2053.  
  2054.  
  2055.     def helpAbout(self):
  2056.         dlg = AboutDialog(self, prop.version, self.toolbox_version + " (Qt4)")
  2057.         dlg.exec_()
  2058.  
  2059.  
  2060.     def __tr(self,s,c = None):
  2061.         return qApp.translate("DevMgr5",s,c)
  2062.  
  2063.  
  2064. class PasswordDialog(QDialog):
  2065.     def __init__(self, prompt, parent=None, name=None, modal=0, fl=0):
  2066.         log.debug("PasswordDialog")
  2067.         QDialog.__init__(self)
  2068.  
  2069.         if not name:
  2070.             self.setObjectName("PasswordDialog")
  2071.  
  2072.         passwordDlg_baseLayout = QGridLayout(self)
  2073.         passwordDlg_baseLayout.setMargin(11)
  2074.         passwordDlg_baseLayout.setSpacing(6)
  2075.         passwordDlg_baseLayout.setObjectName("passwordDlg_baseLayout")
  2076.  
  2077.         self.promptTextLabel = QLabel(self)
  2078.         self.promptTextLabel.setObjectName("promptTextLabel")
  2079.         self.promptTextLabel.setText(prompt)
  2080.         passwordDlg_baseLayout.addWidget(self.promptTextLabel,0,0)
  2081.  
  2082.         self.passwordLineEdit = QLineEdit(self)
  2083.         self.passwordLineEdit.setObjectName("passwordLineEdit")
  2084.         self.passwordLineEdit.setEchoMode(QLineEdit.Password)
  2085.         passwordDlg_baseLayout.addWidget(self.passwordLineEdit,1,0,1,2)
  2086.  
  2087.         spacer1 = QSpacerItem(20,61,QSizePolicy.Minimum,QSizePolicy.Expanding)
  2088.         passwordDlg_baseLayout.addItem(spacer1,2,0)
  2089.  
  2090.         spacer2 = QSpacerItem(321,20,QSizePolicy.Expanding,QSizePolicy.Minimum)
  2091.         passwordDlg_baseLayout.addItem(spacer2,3,0)
  2092.  
  2093.         self.okPushButton = QPushButton(self)
  2094.         self.okPushButton.setObjectName("okPushButton")
  2095.         passwordDlg_baseLayout.addWidget(self.okPushButton,3,1)
  2096.  
  2097.         self.languageChange()
  2098.  
  2099.         self.resize(QSize(420,163).expandedTo(self.minimumSizeHint()))
  2100.  
  2101.         self.connect(self.okPushButton,SIGNAL("clicked()"),self.accept)
  2102.         self.connect(self.passwordLineEdit,SIGNAL("returnPressed()"),self.accept)
  2103.     def getPassword(self):
  2104.         return unicode(self.passwordLineEdit.text())
  2105.  
  2106.     def languageChange(self):
  2107.         self.setWindowTitle(self.__tr("HP Device Manager - Enter Password"))
  2108.         self.okPushButton.setText(self.__tr("OK"))
  2109.  
  2110.     def __tr(self,s,c = None):
  2111.         return qApp.translate("PasswordDialog",s,c)
  2112.  
  2113.  
  2114. def showPasswordUI(prompt):
  2115.     try:
  2116.         dlg = PasswordDialog(prompt, None)
  2117.  
  2118.         if dlg.exec_() == QDialog.Accepted:
  2119.             return dlg.getPassword()
  2120.  
  2121.     finally:
  2122.         pass
  2123.  
  2124.     return ""
  2125.